﻿#ifndef ___PCLIB_S_DES___
#define ___PCLIB_S_DES___


#include "../m_core/m_core.hpp"
#include "s_crypto_base.hpp"

//计算3DES/DES的输入分块长度，输出结果长度。note：密钥长度DES为8字节，3DES为16/24字节
#define  PCDES_LEN        (8)


static const uint32_t PC_DES_SBKEY1[64] =
{
    0x01010400, 0x00000000, 0x00010000, 0x01010404,
    0x01010004, 0x00010404, 0x00000004, 0x00010000,
    0x00000400, 0x01010400, 0x01010404, 0x00000400,
    0x01000404, 0x01010004, 0x01000000, 0x00000004,
    0x00000404, 0x01000400, 0x01000400, 0x00010400,
    0x00010400, 0x01010000, 0x01010000, 0x01000404,
    0x00010004, 0x01000004, 0x01000004, 0x00010004,
    0x00000000, 0x00000404, 0x00010404, 0x01000000,
    0x00010000, 0x01010404, 0x00000004, 0x01010000,
    0x01010400, 0x01000000, 0x01000000, 0x00000400,
    0x01010004, 0x00010000, 0x00010400, 0x01000004,
    0x00000400, 0x00000004, 0x01000404, 0x00010404,
    0x01010404, 0x00010004, 0x01010000, 0x01000404,
    0x01000004, 0x00000404, 0x00010404, 0x01010400,
    0x00000404, 0x01000400, 0x01000400, 0x00000000,
    0x00010004, 0x00010400, 0x00000000, 0x01010004
};

static const uint32_t PC_DES_SBKEY2[64] =
{
    0x80108020, 0x80008000, 0x00008000, 0x00108020,
    0x00100000, 0x00000020, 0x80100020, 0x80008020,
    0x80000020, 0x80108020, 0x80108000, 0x80000000,
    0x80008000, 0x00100000, 0x00000020, 0x80100020,
    0x00108000, 0x00100020, 0x80008020, 0x00000000,
    0x80000000, 0x00008000, 0x00108020, 0x80100000,
    0x00100020, 0x80000020, 0x00000000, 0x00108000,
    0x00008020, 0x80108000, 0x80100000, 0x00008020,
    0x00000000, 0x00108020, 0x80100020, 0x00100000,
    0x80008020, 0x80100000, 0x80108000, 0x00008000,
    0x80100000, 0x80008000, 0x00000020, 0x80108020,
    0x00108020, 0x00000020, 0x00008000, 0x80000000,
    0x00008020, 0x80108000, 0x00100000, 0x80000020,
    0x00100020, 0x80008020, 0x80000020, 0x00100020,
    0x00108000, 0x00000000, 0x80008000, 0x00008020,
    0x80000000, 0x80100020, 0x80108020, 0x00108000
};

static const uint32_t PC_DES_SBKEY3[64] =
{
    0x00000208, 0x08020200, 0x00000000, 0x08020008,
    0x08000200, 0x00000000, 0x00020208, 0x08000200,
    0x00020008, 0x08000008, 0x08000008, 0x00020000,
    0x08020208, 0x00020008, 0x08020000, 0x00000208,
    0x08000000, 0x00000008, 0x08020200, 0x00000200,
    0x00020200, 0x08020000, 0x08020008, 0x00020208,
    0x08000208, 0x00020200, 0x00020000, 0x08000208,
    0x00000008, 0x08020208, 0x00000200, 0x08000000,
    0x08020200, 0x08000000, 0x00020008, 0x00000208,
    0x00020000, 0x08020200, 0x08000200, 0x00000000,
    0x00000200, 0x00020008, 0x08020208, 0x08000200,
    0x08000008, 0x00000200, 0x00000000, 0x08020008,
    0x08000208, 0x00020000, 0x08000000, 0x08020208,
    0x00000008, 0x00020208, 0x00020200, 0x08000008,
    0x08020000, 0x08000208, 0x00000208, 0x08020000,
    0x00020208, 0x00000008, 0x08020008, 0x00020200
};

static const uint32_t PC_DES_SBKEY4[64] =
{
    0x00802001, 0x00002081, 0x00002081, 0x00000080,
    0x00802080, 0x00800081, 0x00800001, 0x00002001,
    0x00000000, 0x00802000, 0x00802000, 0x00802081,
    0x00000081, 0x00000000, 0x00800080, 0x00800001,
    0x00000001, 0x00002000, 0x00800000, 0x00802001,
    0x00000080, 0x00800000, 0x00002001, 0x00002080,
    0x00800081, 0x00000001, 0x00002080, 0x00800080,
    0x00002000, 0x00802080, 0x00802081, 0x00000081,
    0x00800080, 0x00800001, 0x00802000, 0x00802081,
    0x00000081, 0x00000000, 0x00000000, 0x00802000,
    0x00002080, 0x00800080, 0x00800081, 0x00000001,
    0x00802001, 0x00002081, 0x00002081, 0x00000080,
    0x00802081, 0x00000081, 0x00000001, 0x00002000,
    0x00800001, 0x00002001, 0x00802080, 0x00800081,
    0x00002001, 0x00002080, 0x00800000, 0x00802001,
    0x00000080, 0x00800000, 0x00002000, 0x00802080
};

static const uint32_t PC_DES_SBKEY5[64] =
{
    0x00000100, 0x02080100, 0x02080000, 0x42000100,
    0x00080000, 0x00000100, 0x40000000, 0x02080000,
    0x40080100, 0x00080000, 0x02000100, 0x40080100,
    0x42000100, 0x42080000, 0x00080100, 0x40000000,
    0x02000000, 0x40080000, 0x40080000, 0x00000000,
    0x40000100, 0x42080100, 0x42080100, 0x02000100,
    0x42080000, 0x40000100, 0x00000000, 0x42000000,
    0x02080100, 0x02000000, 0x42000000, 0x00080100,
    0x00080000, 0x42000100, 0x00000100, 0x02000000,
    0x40000000, 0x02080000, 0x42000100, 0x40080100,
    0x02000100, 0x40000000, 0x42080000, 0x02080100,
    0x40080100, 0x00000100, 0x02000000, 0x42080000,
    0x42080100, 0x00080100, 0x42000000, 0x42080100,
    0x02080000, 0x00000000, 0x40080000, 0x42000000,
    0x00080100, 0x02000100, 0x40000100, 0x00080000,
    0x00000000, 0x40080000, 0x02080100, 0x40000100
};

static const uint32_t PC_DES_SBKEY6[64] =
{
    0x20000010, 0x20400000, 0x00004000, 0x20404010,
    0x20400000, 0x00000010, 0x20404010, 0x00400000,
    0x20004000, 0x00404010, 0x00400000, 0x20000010,
    0x00400010, 0x20004000, 0x20000000, 0x00004010,
    0x00000000, 0x00400010, 0x20004010, 0x00004000,
    0x00404000, 0x20004010, 0x00000010, 0x20400010,
    0x20400010, 0x00000000, 0x00404010, 0x20404000,
    0x00004010, 0x00404000, 0x20404000, 0x20000000,
    0x20004000, 0x00000010, 0x20400010, 0x00404000,
    0x20404010, 0x00400000, 0x00004010, 0x20000010,
    0x00400000, 0x20004000, 0x20000000, 0x00004010,
    0x20000010, 0x20404010, 0x00404000, 0x20400000,
    0x00404010, 0x20404000, 0x00000000, 0x20400010,
    0x00000010, 0x00004000, 0x20400000, 0x00404010,
    0x00004000, 0x00400010, 0x20004010, 0x00000000,
    0x20404000, 0x20000000, 0x00400010, 0x20004010
};

static const uint32_t PC_DES_SBKEY7[64] =
{
    0x00200000, 0x04200002, 0x04000802, 0x00000000,
    0x00000800, 0x04000802, 0x00200802, 0x04200800,
    0x04200802, 0x00200000, 0x00000000, 0x04000002,
    0x00000002, 0x04000000, 0x04200002, 0x00000802,
    0x04000800, 0x00200802, 0x00200002, 0x04000800,
    0x04000002, 0x04200000, 0x04200800, 0x00200002,
    0x04200000, 0x00000800, 0x00000802, 0x04200802,
    0x00200800, 0x00000002, 0x04000000, 0x00200800,
    0x04000000, 0x00200800, 0x00200000, 0x04000802,
    0x04000802, 0x04200002, 0x04200002, 0x00000002,
    0x00200002, 0x04000000, 0x04000800, 0x00200000,
    0x04200800, 0x00000802, 0x00200802, 0x04200800,
    0x00000802, 0x04000002, 0x04200802, 0x04200000,
    0x00200800, 0x00000000, 0x00000002, 0x04200802,
    0x00000000, 0x00200802, 0x04200000, 0x00000800,
    0x04000002, 0x04000800, 0x00000800, 0x00200002
};

static const uint32_t PC_DES_SBKEY8[64] =
{
    0x10001040, 0x00001000, 0x00040000, 0x10041040,
    0x10000000, 0x10001040, 0x00000040, 0x10000000,
    0x00040040, 0x10040000, 0x10041040, 0x00041000,
    0x10041000, 0x00041040, 0x00001000, 0x00000040,
    0x10040000, 0x10000040, 0x10001000, 0x00001040,
    0x00041000, 0x00040040, 0x10040040, 0x10041000,
    0x00001040, 0x00000000, 0x00000000, 0x10040040,
    0x10000040, 0x10001000, 0x00041040, 0x00040000,
    0x00041040, 0x00040000, 0x10041000, 0x00001000,
    0x00000040, 0x10040040, 0x00001000, 0x00041040,
    0x10001000, 0x00000040, 0x10000040, 0x10040000,
    0x10040040, 0x10000000, 0x00040000, 0x10001040,
    0x00000000, 0x10041040, 0x00040040, 0x10000040,
    0x10040000, 0x10001000, 0x10001040, 0x00000000,
    0x10041040, 0x00041000, 0x00041000, 0x00001040,
    0x00001040, 0x00040040, 0x10000000, 0x10041000
};




/**
*@brief     DES/3DES算法的实现
*/
class CPC3DES
{
public:
    /**
    *@brief		DES/3DES ECB加解密。
    *@param		bEncrypt[in]	加密（true）或解密（false）
    *@param		pSrc[in]	    输入的数据
    *@param		nSrcLen[in]	    输入的数据长度
    *@param		pKey[in]	    密钥
    *@param		nKeyLen[in]	    密钥长度，支持8/16/24字节
    *@param		ePaddingMode[in]	填充模式，参考enum PCPaddingMode
    *@return	二进制串结果，失败为空
    */
    static std::string ECB(bool bEncrypt, const void *pSrc, size_t nSrcLen, const void* pKey, size_t nKeyLen, PCPaddingMode ePaddingMode)
    {
        if (pSrc == NULL || nSrcLen == 0 || pKey == NULL || (nKeyLen != 8 && nKeyLen != 16 && nKeyLen != 24))
        {
            //("input param invalid:pSrc == NULL || nSrcLen == 0 || pKey == NULL || (nKeyLen != 8 && nKeyLen != 16 && nKeyLen != 24)");
            return "";
        }

        unsigned char pszEncLastBlock[PCDES_LEN] = { 0 };
        int nBlockedEncSrcLen = -1;
        if (bEncrypt)
        {
            //如果是加密，则需要填充分组
            nBlockedEncSrcLen = PCCryptPadding(ePaddingMode, (const unsigned char *)pSrc, (unsigned int)nSrcLen, PCDES_LEN, pszEncLastBlock);
            if (nBlockedEncSrcLen < 0)
            {
                //("PCCryptPadding fail");
                return "";
            }
        }
        else
        {
            //如果是解密，则需要校验输入分组长度是否为BLOCK的倍数
            if (nSrcLen % PCDES_LEN != 0)
            {
                //("nSrcLen(%d) %% PCDES_LEN != 0", (int)nSrcLen);
                return "";
            }
        }

        PC_OPTIMIZE_NEWBUF(unsigned char, nSrcLen + 2 * PCDES_LEN + 1, 
        {
            CPC3DES desObj;
            desObj.Init(bEncrypt, pKey, nKeyLen);

            if (bEncrypt)
            {
                int i = 0;
                if (nBlockedEncSrcLen > 0)
                {
                    for (; i < nBlockedEncSrcLen; i += PCDES_LEN)
                    {
                        desObj.CryptBlock((const unsigned char*)pSrc + i, opBuffer + i);
                    }
                }
                desObj.CryptBlock((const unsigned char*)pszEncLastBlock, opBuffer + i);
                return std::string((char*)opBuffer, nBlockedEncSrcLen + PCDES_LEN);
            }
            else
            {
                //如果是解密还需要移除分组
                for (size_t i = 0; i < nSrcLen; i += PCDES_LEN)
                {
                    desObj.CryptBlock((const unsigned char*)pSrc + i, opBuffer + i);
                }
                int nUnpaddingLen = PCCryptUnPadding(ePaddingMode, opBuffer, (unsigned int)nSrcLen, PCDES_LEN);
                if (nUnpaddingLen < 0)
                {
                    //("PCCryptUnPadding fail");
                    return "";
                }
                return std::string((char*)opBuffer, nUnpaddingLen);
            }
        });        
    }

    /**
    *@brief		DES/3DES CBC加解密
    *@param		bEncrypt[in]	加密（true）或解密（false）
    *@param		pSrc[in]	    输入的数据
    *@param		nSrcLen[in]	    输入的数据长度
    *@param		pKey[in]	    密钥
    *@param		nKeyLen[in]	    密钥长度，支持8/16/24字节
    *@param		ePaddingMode[in]	填充模式，参考enum PCPaddingMode
    *@param		szIv[in]        初始向量 NULL代表PCDES_LEN个0x00的字节数组
    *@return	二进制串结果，失败为空
    */
    static std::string CBC(bool bEncrypt, const void *pSrc, size_t nSrcLen, const void* pKey, size_t nKeyLen, PCPaddingMode ePaddingMode, const unsigned char szIv[PCDES_LEN] = NULL)
    {
        if (pSrc == NULL || nSrcLen == 0 || pKey == NULL || (nKeyLen != 8 && nKeyLen != 16 && nKeyLen != 24))
        {
            //("input param invalid:pSrc == NULL || nSrcLen == 0  || pKey == NULL || (nKeyLen != 8 && nKeyLen != 16 && nKeyLen != 24)");
            return "";
        }

        //const IV 转 IV
        unsigned char szRealIV[PCDES_LEN] = { 0 };
        if (szIv != NULL)
        {
            memcpy(szRealIV, szIv, PCDES_LEN);
        }
        else
        {
            memset(szRealIV, 0, PCDES_LEN);
        }

        unsigned char pszEncLastBlock[PCDES_LEN] = { 0 };
        int nBlockedEncSrcLen = -1;
        if (bEncrypt)
        {
            //如果是加密，则需要填充分组
            nBlockedEncSrcLen = PCCryptPadding(ePaddingMode, (const unsigned char *)pSrc, (unsigned int)nSrcLen, PCDES_LEN, pszEncLastBlock);
            if (nBlockedEncSrcLen < 0)
            {
                //("PCCryptPadding fail");
                return "";
            }
        }
        else
        {
            //如果是解密，则需要校验输入分组长度是否为BLOCK的倍数
            if (nSrcLen % PCDES_LEN != 0)
            {
                //("nSrcLen(%d) %% PCDES_LEN != 0", (int)nSrcLen);
                return "";
            }
        }

        PC_OPTIMIZE_NEWBUF(unsigned char, nSrcLen + 2 * PCDES_LEN + 1,
        {
            CPC3DES desObj;
            desObj.Init(bEncrypt, pKey, nKeyLen);

            unsigned char* output = (unsigned char*)opBuffer;
            unsigned char temp[PCDES_LEN];
            if (bEncrypt)
            {
                int i = 0;
                if (nBlockedEncSrcLen > 0)
                {
                    const unsigned char* input = ((const unsigned char*)pSrc);
                    for (; i < nBlockedEncSrcLen; i += PCDES_LEN)
                    {
                        //异或并加密
                        for (int j = 0; j < PCDES_LEN; j++)
                            output[j] = (unsigned char)(input[j] ^ szRealIV[j]);
                        desObj.CryptBlock(output, output);

                        memcpy(szRealIV, output, PCDES_LEN);
                        input += PCDES_LEN;
                        output += PCDES_LEN;
                    }
                }
                for (int j = 0; j < PCDES_LEN; j++)
                    output[j] = (unsigned char)(pszEncLastBlock[j] ^ szRealIV[j]);
                desObj.CryptBlock(output, output);
                return std::string((char*)opBuffer, nBlockedEncSrcLen + PCDES_LEN);
            }
            else
            {
                const unsigned char* input = ((const unsigned char*)pSrc);
                for (size_t i = 0; i < nSrcLen; i += PCDES_LEN)
                {
                    //解密并异或
                    memcpy(temp, input, PCDES_LEN);
                    desObj.CryptBlock(input, output);
                    for (int j = 0; j < PCDES_LEN; j++)
                        output[j] = (unsigned char)(output[j] ^ szRealIV[j]);

                    memcpy(szRealIV, temp, PCDES_LEN);
                    input += PCDES_LEN;
                    output += PCDES_LEN;
                }

                //如果是解密还需要移除分组
                int nUnpaddingLen = PCCryptUnPadding(ePaddingMode, opBuffer, (unsigned int)nSrcLen, PCDES_LEN);
                if (nUnpaddingLen < 0)
                {
                    //("PCCryptUnPadding fail");
                    return "";
                }
                return std::string((char*)opBuffer, nUnpaddingLen);
            }
        });
    }

    /**
    *@brief        标准的DES加解密函数。输入/出数据长度固定为8字节。密钥长度可选8字节(DES)，16字节(双倍长3DES)，24字节(三倍长3DES)。
    *@param        bEncrypt    [in]    是否是加密。true加密，false解密
    *@param        szData      [in]    需加密/解密的数据，固定为8字节
    *@param        szResult    [out]   输出结果，固定为8字节
    *@param        szKey       [in]    加密/解密密钥，可以为8,16,24字节
    *@param        nKeyLen     [in]    加密/解密密钥长度。8,16,24
    *@return       无
    */
    static void Des_STD(bool bEncrypt, const  unsigned char szData[8], unsigned char szResult[8], const unsigned char* szKey, unsigned int nKeyLen)
    {
        CPC3DES desObj;
        desObj.Init(bEncrypt, szKey, nKeyLen);
        desObj.CryptBlock(szData, szResult);
    }

public:
    /***********************************************************************************************************************
    * Routine Description:
    *    对任意长度的输入使用DES/3DES算法采用ANSI X9.9标准计算MAC。
    *    一般来说，ANSI X9.9MAC算法只使用单倍长密钥(Des)。
    *    但有一点需要注意，由于有可能在重置PIN密钥时，新产生的PIN密钥是16字节的双倍长密钥，此时采用双倍长密钥算法(3Des)。
    *    计算过程：
    *    1.MAC数据先按8字节分组，表示为D0～Dn，如果Dn不足8字节时，尾部以指定数据(按标准来说应该是0x00)补齐。
    *    2.初始向量异或D0，用MAC密钥加密异或后的D0，加密结果与D1异或作为下一次的输入。
    *    3.将上一步的加密结果与下一分组异或，然后再用MAC密钥加密。
    *    4.直至所有分组结束，最后一分组的8字节即为MAC值。(有时需要取最后结果的左半部作为MAC，此时MAC长度为4字节。但在此模块中，一律输出8字节MAC)。
    * Arguments:
    *    szInput        [in]        输入数据buffer的指针
    *    nInputLen    [in]        输入数据的长度
    *    szOutput    [IN/OUT]    输出数据的buffer指针，需要调用者分配空间！
    *    nOutputLen    [IN/OUT]    输出数据的长度指针，[in]传入szOutput分配空间长度，[out]传出szOutput的内容长度。建议：分配空间长度 >= 8字节
    *    szkey        [in]        用于加密的密钥
    *    nKeyLen        [in]        输入密钥key的长度，8,16
    *    szIV        [in]        初始向量的指针，默认长度为8字节。如果传入空指针NULL，则默认使用8个字节0作为初始向量。
    *    chPadding    [in]        输入数据不足8字节的整数倍时，填充的数据，一般为0x00
    * Return Value:
    *    0成功，其他为错误码
    ************************************************************************************************************************/
    static CPCResult<> Des_MAC_ANSI99(unsigned char* szInput, unsigned int nInputLen, unsigned char* szOutput, unsigned int* nOutputLen, unsigned char* szKey, unsigned int nKeyLen, unsigned char szIV[8], unsigned char chPadding)
    {
        CPCResult<> result;
        if ((nKeyLen != 8 && nKeyLen != 16) || ((*nOutputLen) < 8))
        {
            return result.SetFail("");
        }

        size_t nCBCRetLen = sizeof(unsigned char) * (nInputLen + 8);
        unsigned char* szCBCRet = (unsigned char*)malloc(nCBCRetLen);
        if (NULL == szCBCRet)
        {
            return result.SetFail("");
        }

        auto encrypted = CPC3DES::CBC(true, szInput, nInputLen, szKey, nKeyLen, ePCPaddingModeNone, szIV);
        if (encrypted.empty())
        {
            return result.SetFail("CPC3DES::CBC fail");
        }
        nCBCRetLen = encrypted.length();
        memcpy(szCBCRet, encrypted.data(), nCBCRetLen);

        //取最后8字节作为MAC
        size_t nLastPos = nCBCRetLen - 8;
        memcpy(szOutput, szCBCRet + nLastPos, 8);
        *nOutputLen = 8;
        
        if (szCBCRet)
        {
            free(szCBCRet);
            szCBCRet = NULL;
        }
        return result;
    }

    /***********************************************************************************************************************
    * Routine Description:
    *    对任意长度的输入使用DES/3DES算法采用ANSI X9.19标准计算MAC。
    *    一般来说，ANSI X9.19MAC算法只使用双倍长密钥。
    *    当密钥长度为单倍长时，ANSI X9.19MAC算法自动变成为ANSI X9.9 MAC算法。
    *    计算过程：
    *    1.MAC数据先按8字节分组，表示为D0～Dn，如果Dn不足8字节时，尾部以指定数据(按标准来说应该是0x00)补齐。
    *    2.初始向量异或D0，用MAC密钥左半部加密异或后的D0，加密结果与D1异或作为下一次的输入。
    *    3.将上一步的加密结果与下一分组异或，然后用MAC密钥左半部加密。
    *    4.直至所有分组结束。
    *    5.用MAC密钥右半部解密(4)的结果。
    *    6.用MAC密钥左半部加密(5)的结果。
    *    7.(6)的结果即为8字节MAC值。(有时需要取最后结果的左半部作为MAC，此时MAC长度为4字节。但在此模块中，一律输出8字节MAC)。
    * Arguments:
    *    szInput        [in]        输入数据buffer的指针
    *    nInputLen    [in]        输入数据的长度
    *    szOutput    [IN/OUT]    输出数据的buffer指针，需要调用者分配空间！
    *    nOutputLen    [IN/OUT]    输出数据的长度指针，[in]传入szOutput分配空间长度，[out]传出szOutput的内容长度。建议：分配空间长度 >= 8字节
    *    szkey        [in]        用于加密的密钥
    *    nKeyLen        [in]        输入密钥key的长度，8,16
    *    szIV        [in]        初始向量的指针，默认长度为8字节。如果传入空指针NULL，则默认使用8个字节0作为初始向量。
    *    chPadding    [in]        输入数据不足8字节的整数倍时，填充的数据，只支持0x00
    * Return Value:
    *    0成功，其他为错误码
    ************************************************************************************************************************/
    static CPCResult<> Des_MAC_ANSI919(unsigned char* szInput, unsigned int nInputLen, unsigned char* szOutput, unsigned int* nOutputLen, unsigned char* szKey, unsigned int nKeyLen, unsigned char szIV[8], unsigned char chPadding)
    {
        CPCResult<> result;
        if ((nKeyLen != 8 && nKeyLen != 16) || ((*nOutputLen) < 8))
        {
            return result.SetFail(" ( nKeyLen != 8  && nKeyLen != 16)   ||   ( (*nOutputLen) < 8 )  ");
        }

        //当密钥长度为单倍长时，ANSI X9.19MAC算法自动变成为ANSI X9.9 MAC算法。
        if (nKeyLen == 8)
        {
            return Des_MAC_ANSI99(szInput, nInputLen, szOutput, nOutputLen, szKey, nKeyLen, szIV, chPadding);
        }

        size_t nCBCRetLen = sizeof(unsigned char) * (nInputLen + 8);
        unsigned char* szCBCRet = (unsigned char*)malloc(nCBCRetLen);
        if (NULL == szCBCRet)
        {
            return result.SetFail("NULL == szCBCRet ");
        }

        //使用左半部分密钥进行CBC运算
        auto encrypted = CPC3DES::CBC(true, szInput, nInputLen, szKey, 8, ePCPaddingModeNone, szIV);
        if (encrypted.empty())
        {
            return result.SetFail("CPC3DES::CBC fail");
        }
        nCBCRetLen = encrypted.length();
        memcpy(szCBCRet, encrypted.data(), nCBCRetLen);
        
        //取CBC运算结果的最后8字节
        size_t nLastPos = nCBCRetLen - 8;
        memcpy(szOutput, szCBCRet + nLastPos, 8);

        //用MAC密钥右半部解密CBC运算结果
        Des_STD(false, szOutput, szOutput, szKey + 8, 8);

        //用MAC密钥左半部加密上一步的结果
        Des_STD(true, szOutput, szOutput, szKey, 8);

        *nOutputLen = 8;
        

        if (szCBCRet)
        {
            free(szCBCRet);
            szCBCRet = NULL;
        }

        return result;
    }


    /***********************************************************************************************************************
    * Routine Description:
    *    对任意长度的输入使用DES/3DES算法采用ANSI X9.9标准，ANSI9.19标准和PBOC填充标准计算MAC。
    *    一般来说，PBOC MAC算法可以使用单倍长密钥(此时采用ANSI X9.9标准)和双倍长密钥(此时采用ANSI X9.19标准)。
    *    计算过程：
    *    1.PBOC填充：将输入数据强制填充0x80，如果填充后的数据按照8字节分组后最后一组依然不够8字节，则用0x00填充。
    *    2.当密钥长度为8时，采用ANSI9.9 MAC将(1)数据计算；当密钥长度为16时，采用ANSI9.19将(1)数据计算出来。
    * Arguments:
    *    szInput        [in]        输入数据buffer的指针
    *    nInputLen    [in]        输入数据的长度
    *    szOutput    [IN/OUT]    输出数据的buffer指针，需要调用者分配空间！
    *    nOutputLen    [IN/OUT]    输出数据的长度指针，[in]传入szOutput分配空间长度，[out]传出szOutput的内容长度。建议：分配空间长度 >= 8字节
    *    szkey        [in]        用于加密的密钥
    *    nKeyLen        [in]        输入密钥key的长度，8,16
    *    szIV        [in]        初始向量的指针，默认长度为8字节。如果传入空指针NULL，则默认使用8个字节0作为初始向量。
    * Return Value:
    *    0成功，其他为错误码
    ************************************************************************************************************************/
    static CPCResult<> Des_MAC_PBOC(unsigned char* szInput, unsigned int nInputLen, unsigned char* szOutput, unsigned int* nOutputLen, unsigned char* szKey, unsigned int nKeyLen, unsigned char szIV[8])
    {
        CPCResult<> result;
        unsigned char szUsingIV[8] = { 0 };    //计算时使用的初始向量

        //参数校验
        if (!szInput || !szOutput || !szKey || !nOutputLen)
        {
            return result.SetFail("!szInput || !szOutput || !szKey || !nOutputLen");
        }
        if (nInputLen < 1 || (nKeyLen != 8 && nKeyLen != 16) || ((*nOutputLen) < 8))
        {
            return result.SetFail("nInputLen < 1  || (nKeyLen != 8 && nKeyLen != 16 ) ||   ( (*nOutputLen) < 8 )");
        }
        if (NULL != szIV)
        {
            memcpy(szUsingIV, szIV, 8);
        }

        unsigned int nPaddingRetLen = sizeof(unsigned char) * (nInputLen + 8);
        unsigned char* szPaddingRet = (unsigned char*)malloc(nPaddingRetLen);
        if (NULL == szPaddingRet)
        {
            return result.SetFail("NULL == szPaddingRet");
        }

        //使用PBOC填充标准填充
        Pub_PaddingData(szInput, nInputLen, szPaddingRet, &nPaddingRetLen, 8, true, 0x80, 0x00);

        //根据密钥长度的不同采用不同的MAC算法
        if (8 == nKeyLen)
        {
            result = Des_MAC_ANSI99(szPaddingRet, nPaddingRetLen, szOutput, nOutputLen, szKey, nKeyLen, szUsingIV, 0x00);
        }
        else
        {
            result = Des_MAC_ANSI919(szPaddingRet, nPaddingRetLen, szOutput, nOutputLen, szKey, nKeyLen, szUsingIV, 0x00);
        }

        if (szPaddingRet)
        {
            free(szPaddingRet);
            szPaddingRet = NULL;
        }

        return result;
    }


    /***********************************************************************************************************************
    * Routine Description:
    *    使用ANSI X9.8标准加密得到PinBlock或解密PinBlock得到用户输入数据(一般是密码)。
    *    PinBlock生成流程：
    *    1.填充用户输入的密码，在密码前填充2个字符的密码长度，在后面填充字符'f'直到16个字符为止；
    *    2.填充用户输入的账号，去掉账号最后一个字符的校验位，然后从后往前取12个字符，在这个数据的最前面填充字符'0'直到16个字符为止；
    *    3.将填充后的密码和账号转换为16进制表示，然后异或这二者得PAN；
    *    4.将PAN用密钥加密，得到PinBlock；
    *    Pinblock解密流程：
    *    1.填充用户输入的账号，去掉账号最后一个字符的校验位，然后从后往前取12个字符，在这个数据的最前面填充字符'0'直到16个字符为止；
    *    2.转换填充后的账号为16进制表示；
    *    3.将用户输入的PinBlock使用密钥解密，得到PAN；
    *    4.异或PAN和第(2)步产生的填充账号，得到密码的填充形式作为输出
    * Arguments:
    *    bEncrypt    [in]        是否是加密。true表示生成PinBlock，false表示将PinBlock数据解密成用户密码明文(不自动去掉填充)。
    *    szInput        [in]        输入数据buffer的指针，加密模式下，一般为用户的密码；解密模式下，一般为PinBlock数据。
    *    nInputLen    [in]        输入数据的长度，加密模式下为用户密码的位数，nInputLen>1且<14;解密模式下，nInputLen=8字节
    *    szOutput    [IN/OUT]    输出数据的buffer指针，需要调用者分配空间！
    *    nOutputLen    [IN/OUT]    输出数据的长度指针，[in]传入szOutput分配空间长度，[out]传出szOutput的内容长度。建议：分配空间长度 >= 8字节
    *    szkey        [in]        用于加密的密钥
    *    nKeyLen        [in]        输入密钥key的长度，密钥可以是单倍长度8字节，做一次DES运算；两倍长度16字节，也可以是三倍长度24字节，做的是3DES运算
    *    szAccount    [in]        主账号完整数据，一般为19位卡号。用字符串表示。以'\0'为字符串结尾。
    * Return Value:
    *    0成功，其他为错误码
    ************************************************************************************************************************/
    static CPCResult<> Des_PinBlock_ANSI98(bool bEncrypt, unsigned char* szInput, unsigned int nInputLen, unsigned char* szOutput, unsigned int* nOutputLen, unsigned char* szKey, unsigned int nKeyLen, char* szAccount)
    {
        //参数校验
        CPCResult<> result;
        if (!szInput || !szOutput || !szKey || !nOutputLen || !szAccount)
        {
            return result.SetFail("!szInput || !szOutput || !szKey || !nOutputLen || !szAccount ");
        }
        if (nKeyLen != 8 && nKeyLen != 16 && nKeyLen != 24)
        {
            return result.SetFail("nKeyLen != 8 && nKeyLen != 16 && nKeyLen != 24  ");
        }
        if (bEncrypt && (nInputLen <1 || nInputLen>14))
        {
            return result.SetFail("bEncrypt && (nInputLen <1 || nInputLen>14)");
        }
        if (!bEncrypt && nInputLen != 8)
        {
            return result.SetFail("!bEncrypt && nInputLen != 8");
        }
        if (strlen(szAccount) < 12 || strlen(szAccount) > 25)
        {
            return result.SetFail("strlen(szAccount) < 12 ||  strlen(szAccount) > 25");
        }
        if ((*nOutputLen) < 8)
        {
            return result.SetFail("(*nOutputLen) < 8");
        }

        char strPaddedAcc[16 + 1] = { 0 };
        int nAccLen = (int)strlen(szAccount);
        int nPaddingStartPos = ((nAccLen == 12) ? 0 : (nAccLen - 1 - 12));

        //填充主账号
        strncpy(strPaddedAcc, "0000000000000000\0", 17);
        strncpy(strPaddedAcc + 4, (char*)(szAccount + nPaddingStartPos), 12);
        std::string vecPaddingBytes = CPCStrUtil::PCDsp2Bytes(strPaddedAcc);

        //生成PinBlock
        if (bEncrypt)
        {
            char strPaddedPin[16 + 1] = { 0 };

            //填充密码
            strncpy(strPaddedPin, "ffffffffffffffff\0", 17);
            snprintf(strPaddedPin, nInputLen + 2, "%02x%s", nInputLen, szInput);
            std::string vecPaddingPin = CPCStrUtil::PCDsp2Bytes(strPaddedPin);

            //异或得PAN
            unsigned char szPAN[8];
            memcpy(szPAN, vecPaddingPin.data(), 8);
            PCXOR2Array((const unsigned char*)vecPaddingBytes.data(), szPAN, 8);

            //加密PAN得PinBlock
            Des_STD(bEncrypt, szPAN, szOutput, szKey, nKeyLen);
        }
        //解密PinBlock
        else
        {
            unsigned char szPan[8] = { 0 };

            //将PinBlock用工作密钥解密得到PAN
            Des_STD(bEncrypt, szInput, szPan, szKey, nKeyLen);

            //将PAN异或主账号的填充数据得到输出数据
            PCXOR2Array((const unsigned char*)vecPaddingBytes.data(), szPan, 8);
            memcpy(szOutput, szPan, 8);
        }
        *nOutputLen = 8;

        return result;
    }


protected:

    //初始化
    void Init(bool bEncrypt, const void* pKey, size_t nKeyLen)
    {
        m_bDes3 = (nKeyLen != 8);
        m_bEncrypt = bEncrypt;

        memset(sk_des, 0x00, sizeof(sk_des));
        memset(sk_des3, 0x00, sizeof(sk_des3));

        if (nKeyLen == 8)
        {
            if (bEncrypt)
            {
                this->pc_des_setkey(sk_des, (const unsigned char*)pKey);
            }
            else
            {
                this->pc_des_setkey(sk_des, (const unsigned char*)pKey);
                for (int i = 0; i < 16; i += 2)
                {
                    PCSWAP2INT(sk_des[i], sk_des[30 - i]);
                    PCSWAP2INT(sk_des[i + 1], sk_des[31 - i]);
                }
            }
        }
        else if (nKeyLen == 16)
        {
            uint32_t sk[96];
            if (bEncrypt)
            {
                this->pc_des3_set2key(sk_des3, sk, (const unsigned char*)pKey);
            }
            else
            {
                this->pc_des3_set2key(sk, sk_des3, (const unsigned char*)pKey);
            }
            memset(sk, 0, sizeof(sk));
        }
        else
        {
            uint32_t sk[96];
            if (bEncrypt)
            {
                this->pc_des3_set3key(sk_des3, sk, (const unsigned char*)pKey);
            }
            else
            {
                this->pc_des3_set3key(sk, sk_des3, (const unsigned char*)pKey);
            }
            memset(sk, 0, sizeof(sk));
        }
    }

    //处理一个分组 分组长度 = PCDES_LEN
    void CryptBlock(const void* pSrc, void* pDest)
    {
        const unsigned char* input = (const unsigned char*)pSrc;
        unsigned char* output = (unsigned char*)pDest;

        ///////////////////////////////////////////////////////////////////////////////
        #define PC_DES_ROUND(X,Y)                              \
        do                                              \
                            {                                               \
        T = *SK++ ^ (X);                            \
        (Y) ^= PC_DES_SBKEY8[ (T      ) & 0x3F ] ^            \
               PC_DES_SBKEY6[ (T >>  8) & 0x3F ] ^            \
               PC_DES_SBKEY4[ (T >> 16) & 0x3F ] ^            \
               PC_DES_SBKEY2[ (T >> 24) & 0x3F ];             \
                                                    \
        T = *SK++ ^ (((X) << 28) | ((X) >> 4));     \
        (Y) ^= PC_DES_SBKEY7[ (T      ) & 0x3F ] ^            \
               PC_DES_SBKEY5[ (T >>  8) & 0x3F ] ^            \
               PC_DES_SBKEY3[ (T >> 16) & 0x3F ] ^            \
               PC_DES_SBKEY1[ (T >> 24) & 0x3F ];             \
                            } while( 0 )

        #define PC_DES_FP(X,Y)                                                       \
        do                                                                    \
                            {                                                                     \
        (X) = (((X) << 31) | ((X) >> 1)) & 0xFFFFFFFF;                    \
        T = ((X) ^ (Y)) & 0xAAAAAAAA; (X) ^= T; (Y) ^= T;                 \
        (Y) = (((Y) << 31) | ((Y) >> 1)) & 0xFFFFFFFF;                    \
        T = (((Y) >>  8) ^ (X)) & 0x00FF00FF; (X) ^= T; (Y) ^= (T <<  8); \
        T = (((Y) >>  2) ^ (X)) & 0x33333333; (X) ^= T; (Y) ^= (T <<  2); \
        T = (((X) >> 16) ^ (Y)) & 0x0000FFFF; (Y) ^= T; (X) ^= (T << 16); \
        T = (((X) >>  4) ^ (Y)) & 0x0F0F0F0F; (Y) ^= T; (X) ^= (T <<  4); \
                            } while( 0 )

        #define PC_DES_IP(X,Y)                                                       \
        do                                                                    \
                            {                                                                     \
        T = (((X) >>  4) ^ (Y)) & 0x0F0F0F0F; (Y) ^= T; (X) ^= (T <<  4); \
        T = (((X) >> 16) ^ (Y)) & 0x0000FFFF; (Y) ^= T; (X) ^= (T << 16); \
        T = (((Y) >>  2) ^ (X)) & 0x33333333; (X) ^= T; (Y) ^= (T <<  2); \
        T = (((Y) >>  8) ^ (X)) & 0x00FF00FF; (X) ^= T; (Y) ^= (T <<  8); \
        (Y) = (((Y) << 1) | ((Y) >> 31)) & 0xFFFFFFFF;                    \
        T = ((X) ^ (Y)) & 0xAAAAAAAA; (Y) ^= T; (X) ^= T;                 \
        (X) = (((X) << 1) | ((X) >> 31)) & 0xFFFFFFFF;                    \
                            } while( 0 )
        ///////////////////////////////////////////////////////////////////////////////
        uint32_t X, Y, T, *SK;

        if (m_bDes3)
            SK = sk_des3;
        else
            SK = sk_des;

        PC_GET_UINT32_BE(X, input, 0);
        PC_GET_UINT32_BE(Y, input, 4);

        PC_DES_IP(X, Y);

        for (int i = 0; i < PCDES_LEN; i++)
        {
            PC_DES_ROUND(Y, X);
            PC_DES_ROUND(X, Y);
        }

        if(m_bDes3)
        {
            for (int i = 0; i < PCDES_LEN; i++)
            {
                PC_DES_ROUND(X, Y);
                PC_DES_ROUND(Y, X);
            }

            for (int i = 0; i < PCDES_LEN; i++)
            {
                PC_DES_ROUND(Y, X);
                PC_DES_ROUND(X, Y);
            }
        }

        PC_DES_FP(Y, X);

        PC_PUT_UINT32_BE(Y, output, 0);
        PC_PUT_UINT32_BE(X, output, 4);

        ///////////////////////////////////////////////////////////////////////////////
        #undef PC_DES_ROUND
        #undef PC_DES_FP
        #undef PC_DES_IP
        ///////////////////////////////////////////////////////////////////////////////
    }

private:

    void pc_des_setkey(uint32_t SK[32], const unsigned char key[PCDES_LEN])
    {
        uint32_t X, Y, T;

        PC_GET_UINT32_BE(X, key, 0);
        PC_GET_UINT32_BE(Y, key, 4);

        /*
        * PC1: left and right halves bit-swap
        */
        static const uint32_t PC_DES_HALVES_BITSWAP_LHs[16] =
        {
            0x00000000, 0x00000001, 0x00000100, 0x00000101,
            0x00010000, 0x00010001, 0x00010100, 0x00010101,
            0x01000000, 0x01000001, 0x01000100, 0x01000101,
            0x01010000, 0x01010001, 0x01010100, 0x01010101
        };

        static const uint32_t PC_DES_HALVES_BITSWAP_RHs[16] =
        {
            0x00000000, 0x01000000, 0x00010000, 0x01010000,
            0x00000100, 0x01000100, 0x00010100, 0x01010100,
            0x00000001, 0x01000001, 0x00010001, 0x01010001,
            0x00000101, 0x01000101, 0x00010101, 0x01010101,
        };

        /*
        * Permuted Choice 1
        */
        T = ((Y >> 4) ^ X) & 0x0F0F0F0F;  X ^= T; Y ^= (T << 4);
        T = ((Y) ^ X) & 0x10101010;  X ^= T; Y ^= (T);

        X = (PC_DES_HALVES_BITSWAP_LHs[(X)& 0xF] << 3) | (PC_DES_HALVES_BITSWAP_LHs[(X >> 8) & 0xF] << 2)
            | (PC_DES_HALVES_BITSWAP_LHs[(X >> 16) & 0xF] << 1) | (PC_DES_HALVES_BITSWAP_LHs[(X >> 24) & 0xF])
            | (PC_DES_HALVES_BITSWAP_LHs[(X >> 5) & 0xF] << 7) |  (PC_DES_HALVES_BITSWAP_LHs[(X >> 13) & 0xF] << 6)
            | (PC_DES_HALVES_BITSWAP_LHs[(X >> 21) & 0xF] << 5) | (PC_DES_HALVES_BITSWAP_LHs[(X >> 29) & 0xF] << 4);

        Y = (PC_DES_HALVES_BITSWAP_RHs[(Y >> 1) & 0xF] << 3) | (PC_DES_HALVES_BITSWAP_RHs[(Y >> 9) & 0xF] << 2)
            | (PC_DES_HALVES_BITSWAP_RHs[(Y >> 17) & 0xF] << 1) | (PC_DES_HALVES_BITSWAP_RHs[(Y >> 25) & 0xF])
            | (PC_DES_HALVES_BITSWAP_RHs[(Y >> 4) & 0xF] << 7)  | (PC_DES_HALVES_BITSWAP_RHs[(Y >> 12) & 0xF] << 6)
            | (PC_DES_HALVES_BITSWAP_RHs[(Y >> 20) & 0xF] << 5) | (PC_DES_HALVES_BITSWAP_RHs[(Y >> 28) & 0xF] << 4);

        X &= 0x0FFFFFFF;
        Y &= 0x0FFFFFFF;

        /*
        * calculate subkeys
        */
        for (int i = 0; i < 16; i++)
        {
            if (i < 2 || i == 8 || i == 15)
            {
                X = ((X << 1) | (X >> 27)) & 0x0FFFFFFF;
                Y = ((Y << 1) | (Y >> 27)) & 0x0FFFFFFF;
            }
            else
            {
                X = ((X << 2) | (X >> 26)) & 0x0FFFFFFF;
                Y = ((Y << 2) | (Y >> 26)) & 0x0FFFFFFF;
            }

            *SK++ = ((X << 4) & 0x24000000) | ((X << 28) & 0x10000000)
                | ((X << 14) & 0x08000000) | ((X << 18) & 0x02080000)
                | ((X << 6) & 0x01000000) | ((X << 9) & 0x00200000)
                | ((X >> 1) & 0x00100000) | ((X << 10) & 0x00040000)
                | ((X << 2) & 0x00020000) | ((X >> 10) & 0x00010000)
                | ((Y >> 13) & 0x00002000) | ((Y >> 4) & 0x00001000)
                | ((Y << 6) & 0x00000800) | ((Y >> 1) & 0x00000400)
                | ((Y >> 14) & 0x00000200) | ((Y)& 0x00000100)
                | ((Y >> 5) & 0x00000020) | ((Y >> 10) & 0x00000010)
                | ((Y >> 3) & 0x00000008) | ((Y >> 18) & 0x00000004)
                | ((Y >> 26) & 0x00000002) | ((Y >> 24) & 0x00000001);

            *SK++ = ((X << 15) & 0x20000000) | ((X << 17) & 0x10000000)
                | ((X << 10) & 0x08000000) | ((X << 22) & 0x04000000)
                | ((X >> 2) & 0x02000000) | ((X << 1) & 0x01000000)
                | ((X << 16) & 0x00200000) | ((X << 11) & 0x00100000)
                | ((X << 3) & 0x00080000) | ((X >> 6) & 0x00040000)
                | ((X << 15) & 0x00020000) | ((X >> 4) & 0x00010000)
                | ((Y >> 2) & 0x00002000) | ((Y << 8) & 0x00001000)
                | ((Y >> 14) & 0x00000808) | ((Y >> 9) & 0x00000400)
                | ((Y)& 0x00000200) | ((Y << 7) & 0x00000100)
                | ((Y >> 7) & 0x00000020) | ((Y >> 3) & 0x00000011)
                | ((Y << 2) & 0x00000004) | ((Y >> 21) & 0x00000002);
        }
    }

    

    void pc_des3_set2key(uint32_t esk[96],
        uint32_t dsk[96],
        const unsigned char key[PCDES_LEN * 2])
    {
       pc_des_setkey(esk, key);
       pc_des_setkey(dsk + 32, key + 8);

        for (int i = 0; i < 32; i += 2)
        {
            dsk[i] = esk[30 - i];
            dsk[i + 1] = esk[31 - i];

            esk[i + 32] = dsk[62 - i];
            esk[i + 33] = dsk[63 - i];

            esk[i + 64] = esk[i];
            esk[i + 65] = esk[i + 1];

            dsk[i + 64] = dsk[i];
            dsk[i + 65] = dsk[i + 1];
        }
    }

    void pc_des3_set3key(uint32_t esk[96],
        uint32_t dsk[96],
        const unsigned char key[24])
    {
        pc_des_setkey(esk, key);
        pc_des_setkey(dsk + 32, key + 8);
        pc_des_setkey(esk + 64, key + 16);

        for (int i = 0; i < 32; i += 2)
        {
            dsk[i] = esk[94 - i];
            dsk[i + 1] = esk[95 - i];

            esk[i + 32] = dsk[62 - i];
            esk[i + 33] = dsk[63 - i];

            dsk[i + 64] = esk[30 - i];
            dsk[i + 65] = esk[31 - i];
        }
    }

private:
    bool m_bDes3;
    bool m_bEncrypt;

    uint32_t sk_des [32];           /*!<  DES subkeys       */
    uint32_t sk_des3[96];           /*!<  3DES subkeys      */
};

#endif    //___PCLIB_S_DES___